/*
 * @Description: 常用JavaScript函数片段
 * @Author: 0001100986/LiuShuai
 * @Date: 2021-06-22 14:09:49
 * @LastEditTime: 2024-01-16 18:31:51
 * @LastEditors: 0001101071/倪泽琪
 */
/* eslint-disable no-sequences */

// 数组
// 数组去重
export function noRepeat (arr) {
  return [...new Set(arr)];
}

// 查找数组最大值
export function arrayMax (arr) {
  return Math.max(...arr);
}

// 查找数组最小值
export function arrayMin (arr) {
  return Math.min(...arr);
}

// 返回已 size 为长度的数组分割的原数组
export function chunk (arr, size = 1) {
  return Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size));
}

// 检查数组中某元素出现的次数
export function countOccurrences (arr, value) {
  return arr.reduce((a, v) => (v === value ? a + 1 : a + 0), 0);
}

// 扁平化数组
export function flatten (arr, depth = -1) {
  if (depth === -1) {
    return [].concat(
      ...arr.map((v) => (Array.isArray(v) ? this.flatten(v) : v))
    );
  }
  if (depth === 1) {
    return arr.reduce((a, v) => a.concat(v), []);
  }
  return arr.reduce(
    (a, v) => a.concat(Array.isArray(v) ? this.flatten(v, depth - 1) : v),
    []
  );
}

// 返回两个数组中不同的元素
export function diffrence (arrA, arrB) {
  return arrA.concat(arrB).filter(function(v, i, arr) {
    return arr.indexOf(v) === arr.lastIndexOf(v);
  });
}

// 返回两个数组中相同的元素
export function intersection (arr1, arr2) {
  return arr2.filter((v) => arr1.includes(v));
}

// 从右删除 n 个元素
export function dropRight (arr, n = 0) {
  return n < arr.length ? arr.slice(0, arr.length - n) : [];
}

// 返回数组中下标间隔 nth 的元素
// 例
// let arr = [1,2,3,4,5,6,7,8,9]
// this.everyNth(arr, 2) 返回[2, 4, 6, 8]
export function everyNth (arr, nth) {
  return arr.filter((v, i) => i % nth === nth - 1);
}

// 返回数组中第 n 个元素（支持负数）
export function nthElement (arr, n = 0) {
  return (n >= 0 ? arr.slice(n, n + 1) : arr.slice(n))[0];
}

// 数组乱排
export function shuffle (arr) {
  let array = arr;
  let index = array.length;

  while (index) {
    index -= 1;
    let randomInedx = Math.floor(Math.random() * index);
    let middleware = array[index];
    array[index] = array[randomInedx];
    array[randomInedx] = middleware;
  }

  return array;
}

// 时间单位：秒  转换 小时/分钟/秒
export function formatTime (time) {
  let hours = Math.floor(time / 3600);
  let minutes = Math.floor(Math.floor(time % 3600) / 60);
  let seconds = Math.floor(time % 60);
  let h = hours.toString().length === 1 ? `0${hours}` : hours;
  let m = minutes.toString().length === 1 ? `0${minutes}` : minutes;
  let s = seconds.toString().length === 1 ? `0${seconds}` : seconds;
  return `${h}:${m}:${s}`;
}

// 浏览器对象BOM

// 统计页面性能
export function pagePerformance () {
  if (!window.performance) return;
  let timing = performance.timing;
  return {
    // DNS解析时间
    dnst: timing.domainLookupEnd - timing.domainLookupStart || 0,
    // TCP建立时间
    tcpt: timing.connectEnd - timing.connectStart || 0,
    // 白屏时间
    wit: timing.responseStart - timing.navigationStart || 0,
    // dom渲染完成时间
    domt: timing.domContentLoadedEventEnd - timing.navigationStart || 0,
    // 页面onload时间
    lodt: timing.loadEventEnd - timing.navigationStart || 0,
    // 页面准备时间
    radt: timing.fetchStart - timing.navigationStart || 0,
    // 页面重定向时间
    rdit: timing.redirectEnd - timing.redirectStart || 0,
    // unload时间
    uodt: timing.unloadEventEnd - timing.unloadEventStart || 0,
    // request请求耗时
    reqt: timing.responseEnd - timing.requestStart || 0,
    // 页面解析dom耗时
    andt: timing.domComplete - timing.domInteractive || 0,
  };
}

// 获取模块名称
export function pathModel (url, data) {
  let path;
  if (url.indexOf('?') > -1) {
    path = url.substring(url.lastIndexOf('#') + 1, url.lastIndexOf('?'));
  } else {
    // eslint-disable-next-line no-unused-vars
    path = url.substring(url.lastIndexOf('#') + 1, url.length);
  }
  let model;
  for (let i = 0; i < data.length; i++) {
    if (data[i].path === path) {
      model = data[i].title;
      return model;
    } else {
      for (let j = 0; j < data[i].children.length; j++) {
        if (data[i].children[j].path === path) {
          model = data[i].children[j].title;
          return model;
        }
      }
    }
  }
}

// 获取浏览器网络速率
export function networkSpeed () {
  new Promise((resolve, reject) => {
    let start = null;
    let end = null;
    let img = document.createElement('img');
    start = new Date().getTime();
    img.onload = function(e) {
      end = new Date().getTime();
      const speed = 3.267578125 * 1000 / (end - start);
      sessionStorage.removeItem('speed');
      sessionStorage.setItem('speed', speed);
      resolve(speed);
    };
    img.src = '/static/images/logo.png';
  }).catch(err => { throw err });
  let result = Number(sessionStorage.getItem('speed'));
  return result;
}

// 获取滚动条位置
export function getScrollPosition (el = window) {
  return {
    // eslint-disable-next-line no-undefined
    x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
    // eslint-disable-next-line no-undefined
    y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop,
  };
}

// 获取 url 中的参数
export function getURLParameters (url) {
  // eslint-disable-next-line no-return-assign
  return url.match(/([^?=&]+)(=([^&]*))/g).reduce((a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {});
}

// 页面跳转，是否记录在 history 中
export function redirect (url, asLink = true) {
  asLink ? (window.location.href = url) : window.location.replace(url);
}

// 滚动条回到顶部动画
export function scrollToTop () {
  const scrollTop =
    document.documentElement.scrollTop || document.body.scrollTop;
  if (scrollTop > 0) {
    window.requestAnimationFrame(scrollToTop);
    // eslint-disable-next-line no-undef
    window.scrollTo(0, c - c / 8);
  } else {
    window.cancelAnimationFrame(scrollToTop);
  }
}

// 复制文本
export function copy (str) {
  const el = document.createElement('textarea');
  el.value = str;
  el.setAttribute('readonly', '');
  el.style.position = 'absolute';
  el.style.left = '-9999px';
  el.style.top = '-9999px';
  document.body.appendChild(el);
  const selected =
    document.getSelection().rangeCount > 0 ?
      document.getSelection().getRangeAt(0) :
      false;
  el.select();
  document.execCommand('copy');
  document.body.removeChild(el);
  if (selected) {
    document.getSelection().removeAllRanges();
    document.getSelection().addRange(selected);
  }
}

// 检测设备类型
export function detectDeviceType () {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  ) ?
    'Mobile' :
    'Desktop';
}

// Cookie
// 增
export function setCookie (key, value, expiredays) {
  let exdate = new Date();
  exdate.setDate(exdate.getDate() + expiredays);
  document.cookie = key + '=' + escape(value) + (expiredays == null ? '' : ';expires=' + exdate.toGMTString());
}

// 删
export function delCookie (name) {
  let exp = new Date();
  exp.setTime(exp.getTime() - 1);
  let cval = getCookie(name);
  if (cval != null) {
    document.cookie = name + '=' + cval + ';expires=' + exp.toGMTString();
  }
}

// 查
export function getCookie (name) {
  let arr;
  let reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
  if ((arr = document.cookie.match(reg))) {
    return arr[2];
  } else {
    return null;
  }
}

// 日期 Date

// 时间戳转换为时间
// 默认为当前时间转换结果
// isMs 为时间戳是否为毫秒
export function timestampToTime (timestamp = Date.parse(new Date()), isMs = true) {
  const date = new Date(timestamp * (isMs ? 1 : 1000));
  return `${date.getFullYear()}-${date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
}

// 文档对象 DOM

/**
 * 固定滚动条
 * 功能描述：一些业务场景，如弹框出现时，需要禁止页面滚动，
 * 这是兼容安卓和 iOS 禁止页面滚动的解决方案
 */
let scrollTop = 0;

export function preventScroll () {
  // 存储当前滚动位置
  scrollTop = window.scrollY;

  // 将可滚动区域固定定位，可滚动区域高度为 0 后就不能滚动了
  document.body.style['overflow-y'] = 'hidden';
  document.body.style.position = 'fixed';
  document.body.style.width = '100%';
  document.body.style.top = -scrollTop + 'px';
  // document.body.style['overscroll-behavior'] = 'none'
}

// 恢复滚动
export function recoverScroll () {
  document.body.style['overflow-y'] = 'auto';
  document.body.style.position = 'static';
  // document.querySelector('body').style['overscroll-behavior'] = 'none'

  window.scrollTo(0, scrollTop);
}

// 判断当前位置是否为页面底部
export function bottomVisible () {
  return (
    document.documentElement.clientHeight + window.scrollY >=
    (document.documentElement.scrollHeight ||
      document.documentElement.clientHeight)
  );
}

// 判断元素是否在可视范围内
// partiallyVisible 为是否为完全可见
export function elementIsVisibleInViewport (el, partiallyVisible = false) {
  const {
    top,
    left,
    bottom,
    right
  } = el.getBoundingClientRect();

  return partiallyVisible ?
    ((top > 0 && top < innerHeight) ||
      (bottom > 0 && bottom < innerHeight)) &&
    ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) :
    top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
}

// 获取元素 css 样式
export function getStyle (el, ruleName) {
  return getComputedStyle(el, null).getPropertyValue(ruleName);
}

// 进入全屏
// launchFullscreen(document.documentElement);
// launchFullscreen(document.getElementById("id")); //某个元素进入全屏
export function launchFullscreen (element) {
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if (element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if (element.msRequestFullscreen) {
    element.msRequestFullscreen();
  } else if (element.webkitRequestFullscreen) {
    element.webkitRequestFullScreen();
  }
}

// 退出全屏
export function exitFullscreen () {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  } else if (document.msExitFullscreen) {
    document.msExitFullscreen();
  } else if (document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if (document.webkitExitFullscreen) {
    document.webkitExitFullscreen();
  }
}

// 全屏事件
document.addEventListener('fullscreenchange', function(e) {
  if (document.fullscreenElement) {
    this.$message.success('全屏模式已开启');
  } else {
    this.$message.success('全屏模式已关闭');
  }
});

// 数字 Number
// 数字千分位分割
export function commafy (num) {
  return num.toString().indexOf('.') !== -1 ?
    num.toLocaleString() :
    num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}

// 生成随机数
export function randomNum (min, max) {
  switch (arguments.length) {
    case 1:
      return parseInt(Math.random() * min + 1, 10);
    case 2:
      return parseInt(Math.random() * (max - min + 1) + min, 10);
    default:
      return 0;
  }
}

// 去除字符串中的html
export function removehtml (str = '') {
  return str.replace(/<[\/\!]*[^<>]*>/ig, '');
}

// 编码base64
export function b64Encode (str) {
  return window.btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
    function toSolidBytes (match, p1) {
      return String.fromCharCode('0x' + p1);
    }));
}

// 解码base64
export function b64Decode (str) {
  if (str) {
    try {
      return decodeURIComponent(window.atob(str).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
      }).join(''));
    } catch (e) {
      // console.error(e);
      return str;
    }
  } else { return str }
}

// GCJ-02 转 BD-09
export function mapTransBMap (lng, lat) {
  let x_pi = 3.14159265358979324 * 3000.0 / 180.0;
  let x = lng;
  let y = lat;
  let z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
  let theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
  let lngs = z * Math.cos(theta) + 0.0065;
  let lats = z * Math.sin(theta) + 0.006;
  return {
    lng: lngs,
    lat: lats
  };
}

// BD-09 转 GCJ-02
export function bMapTransMap (lng, lat) {
  let x_pi = 3.14159265358979324 * 3000.0 / 180.0;
  let x = lng - 0.0065;
  let y = lat - 0.006;
  let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
  let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
  let lngs = z * Math.cos(theta);
  let lats = z * Math.sin(theta);
  return {
    lng: lngs,
    lat: lats
  };
}

// 二进制转xls文件
export function changeFileType (export_data, name) {
  let link = document.createElement('a');
  // type就是blob的type,是MIME类型的，可以自己查看MIME类型都有哪些
  let blogw = new Blob([export_data], { type: 'application/x-xls' });
  // 创建一个新的url对象
  let objectUrl = window.URL.createObjectURL(blogw);
  link.href = objectUrl;
  // 文件命名，可以随便取
  let file_name = name + '.xlsx';
  //  下载的时候自定义的文件名
  link.download = file_name;
  link.click();
  // 为了更好地性能和内存使用状况，应该在适当的时候释放url
  window.URL.revokeObjectURL(objectUrl);
}

// 下载 - 文件流，文件名称，文件类型（自定义）
export function fileDownload (data, name, type, fileType) {
  let link = document.createElement('a');
  let base = ['application/x-xls', 'text/csv', 'application/zip', 'application/octet-stream;'];
  let blob = new Blob([data], { type: base[type] });
  let objectUrl = window.URL.createObjectURL(blob);
  link.href = objectUrl;
  let file_name = name + '.' + fileType;
  link.download = file_name;
  link.click();
  window.URL.revokeObjectURL(objectUrl);
}

// 统一高度
export function howHeight (tab, custom = 0) {
  let vh = document.documentElement.clientHeight;
  // Tabs高度
  let th = tab ? 38 : 0;
  let hg = vh - th - 185 - custom;
  return document.documentElement.clientWidth > 800 ? hg : 500;
}
// IPv6 正则
export const ipv6Pattern = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:)|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}(:[0-9A-Fa-f]{1,4}){1,2})|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){1,3})|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){1,4})|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,5})|([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){1,6})|(:(:[0-9A-Fa-f]{1,4}){1,7})|(([0-9A-Fa-f]{1,4}:){6}(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){0,4}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(:(:[0-9A-Fa-f]{1,4}){0,5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}))|[:][:]|[:][:][/][0-128]$/;

// IPv4正则
export const ipv4Pattern = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;

// IPv4和IPv6混合判断
export const mixedPattern = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/(\d|[1-2]\d|3[0-2]))?$|^([\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^::([\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:):([\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){2}:([\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){3}:([\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){4}:((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^:((:[\da-fA-F]{1,4}){1,6}|:)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^[\da-fA-F]{1,4}:((:[\da-fA-F]{1,4}){1,5}|:)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){2}((:[\da-fA-F]{1,4}){1,4}|:)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){3}((:[\da-fA-F]{1,4}){1,3}|:)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){4}((:[\da-fA-F]{1,4}){1,2}|:)(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){5}:([\da-fA-F]{1,4})?(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|^([\da-fA-F]{1,4}:){6}:(\/([1-9]?\d|(1([0-1]\d|2[0-8]))))?$|[:][:]|[:][:][/][0-128]?$/;

// Mac判断
export const macPattern = /^[a-zA-Z0-9]{2}([-:]?[a-zA-Z0-9]{2})([-:.]?[a-zA-Z0-9]{2})([-:]?[a-zA-Z0-9]{2})([-:.]?[a-zA-Z0-9]{2})([-:]?[a-zA-Z0-9]{2})$/;

// Sn判断
export const snPattern = /^[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}:[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}:[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}:[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}:[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}:[a-zA-Z0-9]{1}[a-zA-Z0-9]{1}$/;


// 密码正则
// 长度限制9~128，不能存在空格，至少包含一个数字、大/小字母及!@#$%^&*()特殊字符
export const pwdRegex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()])(?!.*\s).{9,128}$/;

// 用户名正则
// 长度限制4~60，允许中/英文/数字，禁止使用敏感词Root、Admin、User、Test...等
export const userRegex = /^((?!root|Root|ROOT|test|Test|TEST|user|User|USER|users|Users|USERS|admin|Admin|ADMIN|administrator|Administrator|ADMINISTRATOR|administrators|Administrators|ADMINISTRATORS|system|System|SYSTEM)[\u4e00-\u9fa5a-zA-Z0-9]+){4,60}$/i;

// 重复性连续数字、字母。不区分大小写
export const coherentReg = /([a-zA-Z0-9])\1{3}/i;

// 数字连贯性检测
export const numCoherent = (num) => {
  // 用于连续个数的统计
  let size = 0;
  for (let i = 1; i < num.length; i++) {
    // 等于1代表升序连贯   等于-1代表降序连贯
    if ((num[i] - num[i - 1] === 1) || (num[i] - num[i - 1] === -1)) {
      // 存在连贯：计数+1
      size += 1;
    }
  }
  return size > (num.length - 2);
};

// 字母连贯性检测
export const letterCoherent = (letter) => {
  // 需要验证的字符串
  letter = letter.toUpperCase();
  // 用于连贯个数的计数
  let size = 0;
  for (let i = 1; i < letter.length; i++) {
    // 1正序连贯；-1倒序连贯
    if (((letter[i].charCodeAt()) - (letter[i - 1].charCodeAt()) === 1) || ((letter[i].charCodeAt()) - (letter[i - 1].charCodeAt()) === -1)) {
      // 存在连贯：计数+1
      size += 1;
    }
  }
  // 连贯总数=字符串长度-2；代表全部连贯
  return size > (letter.length - 2);
};